//
//  Transceiver.cpp
//  Simulation
//
//  Created by Christopher Kemsley on 3/5/12.
//  Copyright (c) 2012 iAppolopolis. All rights reserved.
//

#include "Transceiver.hpp"
#include "SimulationParameters.hpp"
#include "Log.hpp"
#include "BroadcastData.hpp"


#define	This	(*this)




char const*	MXStateTable[] = {
	"Idle" ,
	"Line Is Busy" ,
	"Sending RTS" ,
	"Waiting for CTS" ,
	"Receiving (CTS?)" ,
	"Received CTS. Delaying" ,
	"Transmitting Message" ,
	"Waiting for Acknowledge" ,
	"Receiving (ACK?)" ,
	"Complete! Delaying" ,
	"Failed. Delaying" ,
	"Waiting for hidden transmitter"
} ;






using namespace Sim;





Transceiver :: Transceiver	( TransmitterProfile const & profile , DeviceID const & inID , TransceiverDelegate & inDelegate )
:	mDelegate		( inDelegate )
,	mProfile		( profile )
,	mReceiver		( inID , profile , *this )
,	mTransmitter	( inID , *this )

,	mBuffer			( 0 )
,	mLength			( 0 )
,	mTarget			( 0 )

,	mID				( inID )
,	mTimeoutInvocation ()
,	mTimeoutTimer()
,	mState ( kMXStateIdle )
	{
	mTimeoutInvocation = ObjectInvocation<Transceiver>::invocation(this,&Transceiver::_timeout) ;
	}



bool	Transceiver :: isIdle	() const
	{
	return mState == kMXStateIdle ;
	}
bool	Transceiver :: isTransmitting () const
	{
	return mTransmitter.isTransmitting() ;
	}
void	Transceiver :: transmit	( Byte const * data , Length length , DeviceID target )
	{
	switch ( mState )
		{
			case kMXStateIdle : {
				// Copy the data
				mBuffer		=	new Byte [length] ;
				mLength		=	length ;
				memcpy ( (void*)mBuffer , (void const*)data , (size_t)length ) ;
				mTarget = target ;
				
				// Start the TX
				internal_sendRTS () ;
			} break ;
			
			default: {
				ThrowException ( "Transceiver" , "openConnection called, but not idle!" , __FILE__,__LINE__);
			} break ;
		}
	}

void	Transceiver :: setProfile	( TransmitterProfile const & profile )
	{
	mReceiver . setProfile ( profile ) ;
	mProfile = profile ;
	}



void	Transceiver :: operator<<	( MXState const & state )
	{
	Log::states("Transceiver") << "Transition.  From: \"" << MXStateTable[mState] << "\"  To: \"" << MXStateTable[state] << "\"" ;
	if ( state <= mState )
		{
		if ( mState!=kMXStateFailed && mState!=kMXStateCompleted )
			{
			Log::warning("Transciever") << "Transition doesn't go forwards" ;
			}
		}
	mState = state ;
	}




void	Transceiver :: receiveDidBegin ()
	{
	Log::states("Transceiver") << "receiveDidBegin" ;
	
	switch ( mState )
		{
			case kMXStateIdle : {
				This << kMXStateLineIsBusy ;
				mDelegate.lineDidBecomeBusy() ;
			} break ;
			
			case kMXStateWaitingForCTS : {
				This << kMXStateReceivingCTS ;
				internal_stopTimer() ;
			} break ;
			
			case kMXStateWaitingForAck : {
				This << kMXStateReceivingAck ;
				internal_stopTimer() ;
			} break ;
			
			default: {
			} break ;
		}
	}

void	Transceiver :: receiveDidFail ()
	{
	Log::states("Transceiver") << "receiveDidFail" ;
	
	switch ( mState )
		{
			case kMXStateLineIsBusy : {
				This << kMXStateIdle ;
			} break ;
			
			case kMXStateReceivingCTS :
			case kMXStateReceivingAck : {
				internal_markFailed () ;
			} break ;
			
			default: {
			} break ;
		}
	}
void	Transceiver :: receiveDidComplete	( Sim::Message * message )
	{
	Log::states("Transceiver") << "receiveDidComplete" ;
	
	switch ( mState )
		{
			case kMXStateReceivingCTS : {
				BroadcastInfo::Type		messageType ;
				DeviceID				sender ;
				DeviceID				target ;
				
				decodeBroadcast ( message->bytes , messageType , sender , target ) ;
				
				if ( target == mID )
					{
					This << kMXStateReceivedCTS ;
					internal_startSIFSTimer() ;
					}
				else
					{
					internal_markFailed () ;
					}
			} break ;
			
			case kMXStateReceivingAck : {
				BroadcastInfo::Type		messageType ;
				DeviceID				sender ;
				DeviceID				target ;
				
				decodeBroadcast ( message->bytes , messageType , sender , target ) ;
				
				if ( target == mID )
					{
					internal_markComplete() ;
					}
				else
					{
					internal_markFailed() ;
					}
			} break ;
			
			case kMXStateIdle : {
				BroadcastInfo::Type		messageType ;
				DeviceID				sender ;
				DeviceID				target ;
				
				decodeBroadcast ( message->bytes , messageType , sender , target ) ;
				
				// not handling case where we're the target. Not handled in this simulation
				if ( target != mID )
					{
					This << kMXStateHiddenTransmitter ;
					mDelegate.lineDidBecomeBusy() ;
					internal_startHiddenXMitterWait () ;
					}
				else
					{
					ThrowException ( "Transceiver" , "Received a transmission... but not supposed to!" , __FILE__,__LINE__ ) ;
					}
			} break ;
			
			default: {
			} break ;
		}
	}

void	Transceiver :: transmissionDidComplete ()
	{
	Log::states("Transceiver") << "transmissionDidComplete" ;
	
	switch ( mState )
		{
			case kMXStateSendingRTS : {
				mReceiver.enable() ;
				This << kMXStateWaitingForCTS ;
				internal_startDIFSTimer() ;
			} break ;
			
			case kMXStateSendingMessage : {
				mReceiver.enable() ;
				This << kMXStateWaitingForAck ;
				internal_startDIFSTimer() ;
			} break ;
			
			default: {
			} break ;
		}
	}






void	Transceiver :: internal_markFailed ()
	{
	This << kMXStateFailed ;
	delete [] mBuffer ;
	mBuffer = 0 ;
	mLength = 0 ;
	internal_startDIFSTimer() ;
	}
void	Transceiver :: internal_markComplete ()
	{
	This << kMXStateCompleted ;
	delete [] mBuffer ;
	mBuffer = 0 ;
	mLength = 0 ;
	internal_startDIFSTimer() ;
	}


void	Transceiver :: internal_sendRTS ()
	{
	// Update the state
	This << kMXStateSendingRTS ;
	
	// Disable the receiver
	mReceiver.disable() ;
	
	// Create the message
	unsigned length = SimulationParameters::shared().controlPacketSize ;
	Sim::Byte	buffer [length] ;
	encodeRTS ( buffer , length , mID , mTarget ) ;
	
	// Send the message
	mTransmitter . transmit ( buffer,length , mTarget , mProfile ) ;
	}
void	Transceiver :: internal_sendBody ()
	{
	This << kMXStateSendingMessage ;
	
	// Disable the receiver
	mReceiver.disable() ;
	
	// Send the message
	mTransmitter . transmit ( mBuffer , mLength , mTarget , mProfile ) ;
	}


void	Transceiver :: internal_startSIFSTimer ()
	{
	Log::states("Transceiver") << mID << ": Starting SIFS timer" ;
	
	if ( mTimeoutTimer != 0 )
		ThrowException ( "Transeiver" , "startTimer called, but timer exists" , __FILE__,__LINE__ ) ;
	Interval const&	time	=	SimulationParameters::shared().SIFSDuration ;
	mTimeoutTimer << new Timer ( time , mTimeoutInvocation.pointer() ) ;
	Log::states("Transceiver") << mID << ": Duration: " << time ;
	}
void	Transceiver :: internal_startDIFSTimer ()
	{
	Log::states("Transceiver") << mID << ": Starting DIFS timer" ;
	
	if ( mTimeoutTimer != 0 )
		ThrowException ( "Transeiver" , "startTimer called, but timer exists" , __FILE__,__LINE__ ) ;
	Interval const&	time	=	SimulationParameters::shared().DIFSDuration ;
	mTimeoutTimer << new Timer ( time , mTimeoutInvocation.pointer() ) ;
	Log::states("Transceiver") << mID << ": Duration: " << time ;
	}
void	Transceiver :: internal_startHiddenXMitterWait ()
	{
	Log::states("Transceiver") << mID << ": Starting wait-for-some-other-rude-person timer" ;
	
	unsigned long	length		=	SimulationParameters::shared().dataPacketSize + SimulationParameters::shared().controlPacketSize*3 ;
	Interval		duration	=	SimulationParameters::shared() . activityDuration ( length ) ;
	duration					+=	SimulationParameters::shared().SIFSDuration * 3 ;
	
	mTimeoutTimer << new Timer ( duration , mTimeoutInvocation.pointer() ) ;
	
	Log::states("Transceiver") << mID << ": Duration: " << duration ;
	}
void	Transceiver :: internal_stopTimer ()
	{
	Log::states("Transceiver") << "Stopping timer" ;
	
	if ( mTimeoutTimer == 0 )
		ThrowException ( "Transeiver" , "stopTimer called, but timer doesn't exist" , __FILE__,__LINE__ ) ;
	mTimeoutTimer << 0 ;
	}
void	Transceiver :: _timeout ()
	{
	mTimeoutTimer << 0 ;
	
	switch ( mState )
		{
			case kMXStateWaitingForAck :
			case kMXStateWaitingForCTS : {
				This << kMXStateFailed ;
				internal_startDIFSTimer() ;
			} break ;
			
			case kMXStateReceivedCTS : {
				internal_sendBody () ;
			} break ;
			
			case kMXStateFailed : {
				This << kMXStateIdle ;
				mDelegate.connectionDidFail () ;
			} break ;
			
			case kMXStateCompleted : {
				This << kMXStateIdle ;
				mDelegate.connectionDidSucceed () ;
			} break ;
			
			case kMXStateHiddenTransmitter : {
				This << kMXStateIdle ;
				mDelegate.lineDidBecomeOpen() ;
			} break ;
			
			default: {
			} break ;
		}
	}
